Title: SwdTech Suspend
Author: SilentEnigma
Version: 1.0
Release Date: 2023-04-23
Applies to: Final Fantasy III (v1.0) (U)

Archive Contents
-------------------------------

  readme.txt                 = this file

  SwdTechSuspend_H.ips       = patch for headered ROMs
  SwdTechSuspend_H_Anti.ips  = anti-patch for headered ROMs

  SwdTechSuspend_NH.ips      = patch for unheadered ROMs
  SwdTechSuspend_NH_Anti.ips = anti-patch for unheadered ROMs


ROM Addresses
-------------------------------
  C1/0253 - C1/0256, C1/561B - C1/561D, C1/5A45 - C1/5A49, C1/7D41 - C1/7D44,
  C1/7D56 - C1/7D5D, C2/258D - C2/258F


Free Space Used
-------------------------------
  C2/A6A8 - C2/A735, C2/A784 - C2/A7B5 (192 bytes)


Additional RAM used
-------------------------------
  $7EECFC - $7EECFF (4 bytes)


TABLE OF CONTENTS
-------------------------------

0. Description
1. Relevant Offsets & Disassembly
2. Credits
3. Revision History
4. Legal

________________________________________________________________________________

  0. DESCRIPTION
________________________________________________________________________________


This patch modifies the SwdTech charging mechanic in FF3us (FF6) for SNES.

SwdTech is often criticized for its slow charge time and the inability for other
characters to receive commands while charging. Modern rereleases of FF6 address
this by allowing the gauge to charge in the background.

This patch is intended to improve the mechanic in the SNES version by adding
character switching support to the SwdTech gauge window.

While the SwdTech gauge is charging for a character, the player may now press
the X or Y button to switch between characters. The gauge position is held in
the background while other characters receive commands. Once input returns to
the charging character, the SwdTech gauge window automatically reopens. The
gauge will resume from the next tier number past its previous position.

This allows the player to give other characters commands without losing progress
on any charging SwdTech gauge(s).

The feature is disabled if there are no other characters ready for input.

A character's SwdTech gauge resets if the player cancels the gauge window or
the character becomes incapacitated.

This patch uses 4 additional bytes of RAM to hold the value of each character's
SwdTech gauge progress.

________________________________________________________________________________

  1. RELEVANT OFFSETS & DISASSEMBLY
________________________________________________________________________________


Incapacitated character check
-------------------------------

Original:
 C1/0253: 9D 01 40    STA $4001,X   ; battle menu order for ally at index x
 C1/0256: 6B          RTL
Modified:
 C1/0253: 5C A8 A6 C2 JML $C2A6A8   ; new subroutine C2/A6A8

New Subroutine C2/A6A8:
 C2/A6A8: 9D 01 40    STA $4001,X   ; original C1/0253
 C2/A6AB: 9E FC EC    STZ $ECFC,X   ; UNUSED RAM for ally's held SwdTech timer
 C2/A6AE: 6B          RTL


SwdTech command
-------------------------------

Original:
 C1/561B: 9C 82 7B    STZ $7B82     ; (from C1/5529, X = 35) reset SwdTech gauge
Modified:
 C1/561B: EA          NOP           ; typical reset moved to C2/A72A
 C1/561C: EA          NOP
 C1/561D: EA          NOP


Top menu - button check
-------------------------------

Original:
 C1/5A44: D0 03       BNE $5A49
 C1/5A46: 4C 4A 5A    JMP $5A4A
 C1/5A49: 60          RTS
Modified:
 C1/5A44: D0 FA       BNE $5A40     ; to RTS at C1/5A40
 C1/5A46: 5C 09 A7 C2 JML $C2A709   ; new subroutine C2/A709

New Subroutine C2/A709:
; X holds ally ID (0 - 3)
 C2/A709: AD C3 7B    LDA $7BC3     ; pending menu state
 C2/A70C: C9 05       CMP #$05      ; top menu?
 C2/A70E: D0 22       BNE $A732     ; Branch if not
 C2/A710: AD CB 7B    LDA $7BCB     ; should be closing menu?
 C2/A713: D0 1D       BEQ $A732     ; If so, branch
 C2/A715: AE CA 62    LDX $62CA     ; current character
 C2/A718: BD FC EC    LDA $ECFC,X   ; UNUSED RAM held SwdTech gauge value
 C2/A71B: F0 12       BEQ $A72F     ; branch if zero
 C2/A71D: 29 E0       AND #$E0      ; snap counter to start of current number
 C2/A71F: 18          CLC
 C2/A720: 69 20       ADC #$20      ; gauge bonus
 C2/A722: 8D 82 7B    STA $7B82     ; SwdTech counter
 C2/A725: 8A          TXA           ; current character
 C2/A726: 85 22       STA $22       ; multiplier function input
 C2/A728: A9 0C       LDA #$0C      ; cmd info size per ally (4 cmds x 3 bytes)
 C2/A72A: 85 24       STA $24       ; multiplier function input
 C2/A72C: 4C 84 A7    JMP $A784     ; new subroutine C2/A784
 C2/A72F: 9C 82 7B    STZ $7B82     ; reset SwdTech gauge from original C1/561B
 C2/A732: 5C 4A 5A C1 JML $C15A4A   ; replaces original C1/5A46

New Subroutine C2/A784:
 C2/A784: DA          PHX
 C2/A785: 22 D5 18 C1 JSL $C118D5   ; multiplier function
 C2/A789: A6 26       LDX $26       ; product
 C2/A78B: A4 00       LDY $00       ; loop init
 C2/A78D: BD 2E 20    LDA $2E20,X   ; command ID for this ally & command
 C2/A790: C9 07       CMP #$07      ; SwdTech?
 C2/A792: F0 0C       BEQ $A7A0     ; if so, break
 C2/A794: E8          INX           ; 3 info bytes per command
 C2/A795: E8          INX
 C2/A796: E8          INX
 C2/A797: C8          INY
 c2/A798: C0 04 00    CPY #$0004    ; 4 commands
 C2/A79B: D0 F0       BNE $A78D     ; loop management
 C2/A79D: 4C 2F A7    JMP $A72F     ; something went wrong - abort
 C2/A7A0: 98          TYA           ; SwdTech command position for current ally
 C2/A7A1: EE 41 2F    INC $2F41     ; flag - Command has been selected
 C2/A7A4: FA          PLX
 C2/A7A5: 9D 0F 89    STA $890F,X   ; cursor position for this character
 C2/A7A8: 5C C8 7C C1 JML $C17CC8   ; C1/7CC9 - top menu cmd has been selected


Upon confirming SwdTech number
-------------------------------

Original:
 C1/7D41: 4A          LSR A
 C1/7D42: 4A          LSR A
 C1/7D43: 4A          LSR A
 C1/7D44: 4A          LSR A
Modified:
 C1/7D41: 22 F8 A6 C2 JSL $C2A6F8   ; new subroutine C2/A6F8

New Subroutine C2/A6EF:
; Reset held gauge value for current ally
 C2/A6EF: DA          PHX
 C2/A6F0: AE 01 02    LDX $0201     ; current ally ID (0 - 3)
 C2/A6F3: 9E FC EC    STZ $ECFC,X   ; UNUSED RAM reset held SwdTech gauge value
 C2/A6F6: FA          PLX
 C2/A6F7: 60          RTS

New Subroutine C2/A6F8:
; Reset held gauge, etc.
 C2/A6F8: 20 EF A6    JSR $A6EF     ; new subroutine C2/A6EF - reset held gauge
 C2/A6FB: 4A          LSR A         ; original C1/7D41
 C2/A6FC: 4A          LSR A         ; original C1/7D42
 C2/A6FD: 4A          LSR A         ; original C1/7D43
 C2/A6FE: 4A          LSR A         ; original C1/7D44
 C2/A6FF: 6B          RTL


In SwdTech gauge - button check
-------------------------------

Original:
 C1/7D56: A5 09       LDA $09       ; Pressed mapped B this frame (& not last)?
 C1/7D58: 10 05       BPL $7D5F     ; Branch if not
Modified:
 C1/7D56: 5C B9 A6 C2 JML $C2A6B9   ; new subroutine C2/A6B9

New Subroutine C2/A6AF:
; hold current gauge value
 C2/A6AF: AE 01 02    LDX $0201     ; current ally ID (0 - 3)
 C2/A6B2: AD 82 7B    LDA $7B82     ; SwdTech counter
 C2/A6B5: 9D FC EC    STA $ECFC,X   ; UNUSED RAM hold SwdTech counter value
 C2/A6B8: 60          RTS

New Subroutine C2/A7A3:
; check for available allies & X/Y button press
 C2/A6B9: A5 09       LDA $09       ; original C1/7D56
 C2/A6BB: 10 04       BPL $A7AB     ; replaces original C1/7D58
 C2/A6BD: 5C 5A 7D C1 JML $C17D5A   ; Player has canceled SwdTech gauge
 C2/A6C1: DA          PHX
 C2/A6C2: A2 03 00    LDX #$0003    ; loop init
 C2/A6C5: BD 01 40    LDA $4001,X   ; character order
 C2/A6C8: 3A          DEA           ; Is ally current (00) or inactive (FF)?
 C2/A6C9: 10 05       BPL $A6D0     ; If not, branch to button check
 C2/A6CB: CA          DEX
 C2/A6CC: 10 F7       BPL $A6C5     ; loop management
 C2/A6CE: 80 1A       BRA $A6EA     ; No ally available; branch to abort
 C2/A6D0: A5 09       LDA $09       ; buttons just pressed this frame (incl. Y)
 C2/A6D2: 0A          ASL A         ; Y pressed this frame (but not last)?
 C2/A6D3: 10 08       BPL $A6DD     ; Branch if not
 C2/A6D5: 20 AF A6    JSR $A6AF     ; new subroutine C2/A6AF - hold gauge value
 C2/A6D8: FA          PLX
 C2/A6D9: 5C C0 7A C1 JML $C17AC0   ; C1/7AC0 - Y press at top menu
 C2/A6DD: A5 08       LDA $08       ; buttons just pressed this frame (incl. X)
 C2/A6DF: 0A          ASL A         ; X pressed this frame (but not last)?
 C2/A6E0: 10 08       BPL $A6EA     ; Branch if not
 C2/A6E2: 20 AF A6    JSR $A6AF     ; new subroutine C2/A6AF - Hold gauge value
 C2/A6E5: FA          PLX
 C2/A6E6: 5C AF 7A C1 JML $C17AB1   ; C1/7AB1 - X press at top menu
 C2/A6EA: FA          PLX
 C2/A6EB: 5C 5F 7D C1 JML $C17D5F   ; C1/7D5F - abort


Upon canceling SwdTech gauge
-------------------------------

Original:
 C1/7D5A: E6 96       INC $96       ; player has canceled SwdTech gauge
 C1/7D5C: 4C 28 56    JMP $5628     ; close SwdTech gauge window
Modified:
 C1/7D5A: 5C 00 A7 C2 JML $C2A700   ; new subroutine C2/A700

New Subroutine C2/A700:
 C2/A700: 20 EF A6    JSR $A6EF     ; new subroutine C2/A6EF - reset held gauge
 C2/A703: E6 96       INC $96       ; original C1/7D5A
 C2/A705: 5C 28 56 C1 JML $C15628   ; replaces original C1/7D5C


Battle initialization
-------------------------------

Original:
 C2/258D: A9 FF 03     LDA #$03FF   ; 10 bits set, 10 possible entities in battle
Modified:
 C2/258D: 20 AC A7     JSR $A7AC    ; new subroutine C2/A7AC

New Subroutine C2/A7AC:
 C2/A7AC: 9C FC EC     STZ #$ECFC   ; UNUSED RAM held SwdTech gauge (slot 1 & 2)
 C2/A7AF: 9C FE EC     STZ #$ECFE   ; UNUSED RAM held SwdTech gauge (slot 3 & 4)
 C2/A7B2: A9 FF 03     LDA #$03FF   ; original C2/258D
 C2/A7B5: 60           RTS

________________________________________________________________________________

  2. CREDITS
________________________________________________________________________________

Special Thanks:

  Imzogelmo, for his disassembly of bank C1
  http://www.angelfire.com/al2/imzogelmo/patches.html

  C-Dude, for pointing out a plethora of unused battle RAM
________________________________________________________________________________

  3. REVISION HISTORY
________________________________________________________________________________


2023-04-23 : Version 1.0 released

________________________________________________________________________________

  4. LEGAL
________________________________________________________________________________


Copyright (C) 2023 David R. Thompson (SilentEnigma).

The copyright holder ("author") permits the free use of the attributed work
referenced by this document exclusively for non-commercial purposes, provided
that the following conditions are met:
1. The author and all contributors credited in this readme document shall be
 given credit for their respective contributions wherever the attributed work is
 reused, redistributed, or modified.
2. This readme document shall accompany any of the files comprising the
 attributed work wherever they are redistributed in unmodified form.

The work(s) and file(s) distributed with this document are provided "AS-IS",
WITHOUT ANY WARRANTY. The author shall not be held responsible for any damages
related to the use of work(s) and file(s) distributed with this document.
